// Fill out your copyright notice in the Description page of Project Settings.


#include "GameModeBaseCpp.h"

#include "AttackComponentBaseCpp.h"
#include "GameInstanceBaseCpp.h"
#include "DamageTable.h"
#include "PlayerBaseCpp.h"

AGameModeBaseCpp::AGameModeBaseCpp()
{
	KnifeObjPoolCapacity = 10;
	KnifeObjPool.Init(nullptr, KnifeObjPoolCapacity);
	Front = 0;
	Tail = 0;
	PoolLength = 0;
	FirePoolFront = 0;
	FirePoolTail = 0;
	FirePoolLength = 0;
	FireObjPoolCapacity = 10;
	FireObjPool.Init(nullptr, FireObjPoolCapacity);
	const ConstructorHelpers::FObjectFinder<UBlueprint> FindObject(TEXT("Blueprint'/Game/Blueprints/Knife_BP.Knife_BP'"));
	if(FindObject.Object)
	{
		KnifeBP = FindObject.Object->GeneratedClass;
	}
	const ConstructorHelpers::FObjectFinder<UBlueprint> FindObject1(TEXT("Blueprint'/Game/Blueprints/Fire_BP.Fire_BP'"));
	if(FindObject1.Object)
	{
		FireBP = FindObject1.Object->GeneratedClass;
	}
	ReadDamageTable();
}

void AGameModeBaseCpp::SetDeadUITimer()
{
	const FTimerDelegate DeadUITimerDelegate = FTimerDelegate::CreateUObject(this, &AGameModeBaseCpp::DeadUITimerCallback);
	GetWorldTimerManager().SetTimer(DeadUITimerHandle, DeadUITimerDelegate, 3.0f, false);
	UGameInstanceBaseCpp* CurrentGameInstance = Cast<UGameInstanceBaseCpp>(GetGameInstance());
	CurrentGameInstance->ShowDeadUI();
}

void AGameModeBaseCpp::DeadUITimerCallback()
{
	GetWorldTimerManager().ClearTimer(DeadUITimerHandle);
	UGameInstanceBaseCpp* CurrentGameInstance = Cast<UGameInstanceBaseCpp>(GetGameInstance());
	CurrentGameInstance->LoadMapByMapId(CurrentGameInstance->GetCurrentMapId());
}

AKnifeBaseCpp* AGameModeBaseCpp::GetKnifeObjFromPool(const FVector& SpawnLocation, const FVector& SpawnDirection)
{
	if (PoolLength <= 0)
	{
		AKnifeBaseCpp* Obj = GetWorld()->SpawnActor<AKnifeBaseCpp>(KnifeBP, SpawnLocation + 8.0f * SpawnDirection, FRotator::ZeroRotator);
		if (Obj == nullptr)
		{
			return nullptr;
		}
		Obj->SetFlyParam(SpawnLocation + 8.0f * SpawnDirection, SpawnDirection);
		return Obj;
	}
	KnifeObjPool[Front]->SetFlyParam(SpawnLocation + 8.0f * SpawnDirection, SpawnDirection);
	const auto E =  KnifeObjPool[Front];
	Front = (Front + 1) % KnifeObjPoolCapacity;
	PoolLength--;
	return E;
}

AFireBaseCpp* AGameModeBaseCpp::GetFireObjFromPool(const FVector& SpawnLocation)
{
	if (FirePoolLength <= 0)
	{
		AFireBaseCpp* Obj = GetWorld()->SpawnActor<AFireBaseCpp>(FireBP, SpawnLocation, FRotator::ZeroRotator);
		if (Obj == nullptr)
		{
			return nullptr;
		}
		Obj->SetParam();
		return Obj;
	}
	FireObjPool[FirePoolFront]->SetParam();
	const auto E = FireObjPool[FirePoolFront];
	FirePoolFront = (FirePoolFront + 1) % FireObjPoolCapacity;
	FirePoolLength--;
	return E;
}

bool AGameModeBaseCpp::TryPutKnifeObjToPool(AKnifeBaseCpp* Obj)
{
	if (PoolLength == KnifeObjPoolCapacity)
	{
		Obj->Destroy();
		return false;
	}
	Obj->SetLock();
	KnifeObjPool[Tail] = Obj;
	Tail = (Tail + 1) % KnifeObjPoolCapacity;
	PoolLength++;
	return true;
}

bool AGameModeBaseCpp::TryPutFireObjToPool(AFireBaseCpp* Obj)
{
	if (FirePoolLength == FireObjPoolCapacity)
	{
		Obj->Destroy();
		return false;
	}
	Obj->SetLock();
	FireObjPool[FirePoolTail] = Obj;
	FirePoolTail = (FirePoolTail + 1) % FireObjPoolCapacity;
	FirePoolLength++;
	return true;
}

void AGameModeBaseCpp::StartAttackPro(const int& Amount)
{
	ComboAmount = Amount;
	ComboCnt = 0;
	const FTimerDelegate ComboTimerDelegate = FTimerDelegate::CreateUObject(this, &AGameModeBaseCpp::AttackProCallback);
	GetWorldTimerManager().SetTimer(ComboTimerHandle, ComboTimerDelegate, 0.2f, true);
}

void AGameModeBaseCpp::AttackProCallback()
{
	APlayerBaseCpp* CurrentPlayer = Cast<APlayerBaseCpp>(GetWorld()->GetFirstPlayerController()->GetPawn());
	if (ComboCnt != ComboAmount)
	{
		ComboCnt++;
		const FVector PlayerLocation = CurrentPlayer->GetActorLocation();
		FVector PlayerDirection = FVector::ZeroVector;
		if (CurrentPlayer->AnimState == EAnimEnum::E_IdleLeft || CurrentPlayer->AnimState == EAnimEnum::E_JumpLeft || CurrentPlayer->AnimState == EAnimEnum::E_RunLeft)
		{
			PlayerDirection = FVector(-1.0f, 0.0f, 0.0f);
			const auto Obj = GetKnifeObjFromPool(PlayerLocation, PlayerDirection);
			Obj->SetMagicColor(CurrentPlayer->GetAttackComponent()->GetUseMagicBuffer());
		}
		else if (CurrentPlayer->AnimState == EAnimEnum::E_IdleRight || CurrentPlayer->AnimState == EAnimEnum::E_JumpRight || CurrentPlayer->AnimState == EAnimEnum::E_RunRight)
		{
			PlayerDirection = FVector(1.0f, 0.0f, 0.0f);
			const auto Obj = GetKnifeObjFromPool(PlayerLocation, PlayerDirection);
			Obj->SetMagicColor(CurrentPlayer->GetAttackComponent()->GetUseMagicBuffer());
		}
	}
	else
	{
		ComboCnt = 0;
		if (CurrentPlayer->GetAttackComponent()->GetUseMagicBuffer())
		{
			CurrentPlayer->GetAttackComponent()->SetUseMagicBuffer(false);
		}
		GetWorldTimerManager().ClearTimer(ComboTimerHandle);
	}
}

void AGameModeBaseCpp::ReadDamageTable()
{
	DamageTable = LoadObject<UDataTable>(nullptr, TEXT("/Game/DataConfig/DamageTable.DamageTable"));
	if (DamageTable == nullptr)
	{
		return;
	}
	for (auto RowIt = DamageTable->GetRowMap().CreateConstIterator(); RowIt; ++RowIt)
	{
		FDamageTable* TableRowValue = (FDamageTable*)RowIt->Value;
		if (TableRowValue == nullptr)
		{
			return;
		}
		if (DamageMap.Contains(TableRowValue->PawnType))
		{
			DamageMap[TableRowValue->PawnType].AddToPawnDamageMap(TableRowValue->AttackMode, TableRowValue->DamageValue);
		}
		else
		{
			FPawnDamageInfo Info;
			Info.AddToPawnDamageMap(TableRowValue->AttackMode, TableRowValue->DamageValue);
			DamageMap.Add(TableRowValue->PawnType, Info);
		}
	}
}

bool AGameModeBaseCpp::GetPawnDamageMap(const EPawnType PawnType, const EAttackMode AttackMode, float& Value)
{
	if (!DamageMap.Contains(PawnType))
	{
		return false;
	}
	FPawnDamageInfo Info = DamageMap[PawnType];
	if (!Info.PawnDamageMap.Contains(AttackMode))
	{
		return false;
	}
	Value = Info.PawnDamageMap[AttackMode];
	return true;
}

